#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/sm4.h>
#include <gmssl/rand.h>

#define BUFFER_SIZE 4096

typedef struct {
	    uint8_t encrypted_key[SM2_MAX_CIPHERTEXT_SIZE];
	        size_t encrypted_key_len;
		    uint8_t ciphertext[BUFFER_SIZE];
		        size_t ciphertext_len;
			    uint8_t hash[SM3_DIGEST_SIZE];
} DigitalEnvelope;

void print_hex(const char *label, const uint8_t *data, size_t len) {
	    printf("%s: ", label);
	        for(size_t i = 0; i < len; i++) {
			        printf("%02X", data[i]);
				    }
		    printf("\n");
}

int create_digital_envelope(const char *plaintext, size_t plaintext_len, 
		                           const SM2_KEY *pub_key, DigitalEnvelope *env) {
	   
	    uint8_t sm4_key[SM4_KEY_SIZE];
	        if (rand_bytes(sm4_key, sizeof(sm4_key)) != 1) {
			        fprintf(stderr, "生成SM4密钥失败\n");
				        return 0;
					    }
		    
		    SM3_CTX sm3_ctx;
		        sm3_init(&sm3_ctx);
			    sm3_update(&sm3_ctx, (const uint8_t *)plaintext, plaintext_len);
			        sm3_finish(&sm3_ctx, env->hash);
				    
				    SM4_KEY sm4_enc_key;
				        sm4_set_encrypt_key(&sm4_enc_key, sm4_key);
					    
					    uint8_t iv[SM4_BLOCK_SIZE];
					        if (rand_bytes(iv, sizeof(iv)) != 1) {
							        fprintf(stderr, "生成IV失败\n");
								        return 0;
									    }
						    
						    size_t outlen;
						        if (sm4_cbc_padding_encrypt(&sm4_enc_key, iv, 
										                               (const uint8_t *)plaintext, plaintext_len,
													                                      env->ciphertext, &env->ciphertext_len) != 1) {
								        fprintf(stderr, "SM4加密失败\n");
									        return 0;
										    }
							    
							    if (sm2_encrypt(pub_key, sm4_key, sizeof(sm4_key),
										                       env->encrypted_key, &env->encrypted_key_len) != 1) {
								            fprintf(stderr, "SM2加密失败\n");
									            return 0;
										        }
							        
							        return 1;
}

int open_digital_envelope(const DigitalEnvelope *env, 
		                         const SM2_KEY *priv_key,
					                          uint8_t *plaintext, size_t *plaintext_len) {
	 
	    uint8_t sm4_key[SM4_KEY_SIZE];
	        size_t sm4_key_len;
		    
		    if (sm2_decrypt(priv_key, env->encrypted_key, env->encrypted_key_len,
					                       sm4_key, &sm4_key_len) != 1) {
			            fprintf(stderr, "SM2解密失败\n");
				            return 0;
					        }

		        SM4_KEY sm4_dec_key;
			    sm4_set_decrypt_key(&sm4_dec_key, sm4_key);
			        
			        uint8_t iv[SM4_BLOCK_SIZE] = {0}; // 实际应用中应从信封中获取IV
				    
				    if (sm4_cbc_padding_decrypt(&sm4_dec_key, iv,
							                                   env->ciphertext, env->ciphertext_len,
											                                  plaintext, plaintext_len) != 1) {
					            fprintf(stderr, "SM4解密失败\n");
						            return 0;
							        }
				        
				        uint8_t calc_hash[SM3_DIGEST_SIZE];
					    SM3_CTX sm3_ctx;
					        sm3_init(&sm3_ctx);
						    sm3_update(&sm3_ctx, plaintext, *plaintext_len);
						        sm3_finish(&sm3_ctx, calc_hash);
							    
							    if (memcmp(calc_hash, env->hash, SM3_DIGEST_SIZE) != 0) {
								            fprintf(stderr, "哈希验证失败\n");
									            return 0;
										        }
							        
							        return 1;
}

int main() {
	    
	    SM2_KEY sm2_key;
	        if (sm2_key_generate(&sm2_key) != 1) {
			        fprintf(stderr, "SM2密钥生成失败\n");
				        return 1;
					    }
		    
		    const char *message = "这是一条通过数字信封传输的秘密消息";
		        size_t message_len = strlen(message);

			    DigitalEnvelope env;
			        if (!create_digital_envelope(message, message_len, &sm2_key, &env)) {
					        return 1;
						    }
				    
				    printf("数字信封创建成功\n");
				        print_hex("加密的SM4密钥", env.encrypted_key, env.encrypted_key_len);
					    print_hex("SM3哈希", env.hash, SM3_DIGEST_SIZE);
					        printf("密文长度: %zu字节\n", env.ciphertext_len);
						    
						    uint8_t decrypted[BUFFER_SIZE];
						        size_t decrypted_len;
							    
							    if (!open_digital_envelope(&env, &sm2_key, decrypted, &decrypted_len)) {
								            return 1;
									        }
							        
							        decrypted[decrypted_len] = '\0';
								    printf("\n解密成功:\n%s\n", decrypted);
								        
								        return 0;
}
